#ifdef WITH_CG
#ifdef WITH_OPENGL

#include "shader_cg_opengl.h"
#include "texture_opengl.h"
#include "render_target_opengl.h"

using namespace krryn::system_imp;
using namespace krryn::math;
using namespace krryn::view;

shader_cg_opengl::shader_cg_opengl(shader_catalog_cg &a_Catalog, const std::string &a_Filename, CGcontext a_Context) : shader_cg(a_Catalog, a_Filename, a_Context){
	m_VertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(m_VertexProfile);

	m_PixelProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(m_PixelProfile);

	init(a_Filename);

	cgGLLoadProgram(m_VertexShader);
	cgGLLoadProgram(m_PixelShader);
}

void shader_cg_opengl::bind_textures_of_render_target(shader_type a_Type, krryn::core::refc_ptr<render_target> a_Targets){
	render_target_opengl *l_glTargets = (render_target_opengl*)a_Targets.get();

	std::vector<std::string> l_Names = l_glTargets->get_attachment_names();

	for(size_t i = 0; i < l_Names.size(); i++){
		if(!m_Params[a_Type][l_Names[i]]){
			throw_shader_param_error(a_Type, l_Names[i]);
		}else{
			glActiveTextureARB(GL_TEXTURE0_ARB + i);
			GLuint l_TexId = l_glTargets->get_texture_id(l_Names[i]);
			glBindTexture(GL_TEXTURE_2D, l_TexId);
			cgGLSetTextureParameter(m_Params[a_Type][l_Names[i]], l_TexId);
		}
	}
}

void shader_cg_opengl::bind_texture2d(shader_type a_Type, const std::string &a_Name, texture *a_Texture){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetTextureParameter(m_Params[a_Type][a_Name], ((texture_opengl*)a_Texture)->get_opengl_id());
	}
}

void shader_cg_opengl::bind_matrix4x4(shader_type a_Type, const std::string &a_Name, const matrix &a_Matrix){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetMatrixParameterfc(m_Params[a_Type][a_Name], a_Matrix.cell);
	}
}

void shader_cg_opengl::bind_float4(shader_type a_Type, const std::string &a_Name, vector4f a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameter4fv(m_Params[a_Type][a_Name], &a_Vector.m_X);
	}
}

void shader_cg_opengl::bind_float3(shader_type a_Type, const std::string &a_Name, vector3f a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameter3fv(m_Params[a_Type][a_Name], &a_Vector.m_X);
	}
}

void shader_cg_opengl::bind_float2(shader_type a_Type, const std::string &a_Name, uv a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameter2fv(m_Params[a_Type][a_Name], &a_Vector.m_X);
	}
}

void shader_cg_opengl::bind_float(shader_type a_Type, const std::string &a_Name, float a_Scalar){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameter1f(m_Params[a_Type][a_Name], a_Scalar);
	}
}

/////////////////// ARRAY functions

void shader_cg_opengl::bind_matrix4x4_array(shader_type a_Type, const std::string &a_Name, int a_Count, matrix *a_Matrix){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetMatrixParameterArrayfc(m_Params[a_Type][a_Name], 0, a_Count, (float *)a_Matrix);
	}
}

void shader_cg_opengl::bind_float4_array(shader_type a_Type, const std::string &a_Name, int a_Count, vector4f *a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameterArray4f(m_Params[a_Type][a_Name], 0, a_Count, (float*)a_Vector);
	}
}

void shader_cg_opengl::bind_float3_array(shader_type a_Type, const std::string &a_Name, int a_Count, vector3f *a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameterArray3f(m_Params[a_Type][a_Name], 0, a_Count, (float*)a_Vector);
	}
}

void shader_cg_opengl::bind_float2_array(shader_type a_Type, const std::string &a_Name, int a_Count, uv *a_Vector){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameterArray2f(m_Params[a_Type][a_Name], 0, a_Count, (float*)a_Vector);
	}
}

void shader_cg_opengl::bind_float_array(shader_type a_Type, const std::string &a_Name, int a_Count, float *a_Scalar){
	if(!m_Params[a_Type][a_Name]){
		throw_shader_param_error(a_Type, a_Name);
	}else{
		cgGLSetParameterArray1f(m_Params[a_Type][a_Name], 0, a_Count, (float*)a_Scalar);
	}
}

void shader_cg_opengl::enable(){
	//m_Projection = cgGetNamedParameter(m_VertexShader, "projection");
	//cgGLSetStateMatrixParameter(m_Projection, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY );

	for(size_t i = 0; i < shader_type_count; i++){
		std::map<std::string, CGparameter>::iterator j = m_Params[i].begin();
		for(; j != m_Params[i].end(); j++){
			shader_auto_parameter* const l_AutoParam = m_Catalog.get_auto_parameter(j->first);
			if(l_AutoParam){
				l_AutoParam->configure_shader((view::shader_type)i, *this);
			}
		}
	}

	cgGLEnableProfile(m_VertexProfile);
	cgGLBindProgram(m_VertexShader);

	cgGLEnableProfile(m_PixelProfile);
	cgGLBindProgram(m_PixelShader);
}

void shader_cg_opengl::disable(){
	cgGLDisableProfile(m_VertexProfile);
	cgGLDisableProfile(m_PixelProfile);
}

#endif
#endif
